﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Configuration;
using System.Web.Configuration;
using System.Data;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Threading;
using Microsoft.JScript;
using System.Reflection;
using Rookey.Frame.IMCore.IO;

namespace Rookey.Frame.IMCore
{
	/// <summary>
	///MessageManagement 的摘要说明
	/// </summary>
	public class MessageImpl
	{
		static MessageImpl m_Instance = new MessageImpl();

		static public MessageImpl Instance
		{
			get { return m_Instance; }
		}

		static MessageImpl()
		{
			Instance.Initialize(HttpContext.Current);
		}

		private MessageImpl()
        {
            m_Timer = new Timer(this.TimerProc);
            m_Timer.Change(0, 2 * 60 * 1000);
        }

        Timer m_Timer = null;

        private void TimerProc(object state)
        {
            WriteCache();
        }

		Object m_Lock = new Object();
		Int64 m_MaxKey = 1;
		DateTime m_MaxCreatedTime = DateTime.Now;

#		if DEBUG
		const Int32 MAX_CACHE_COUNT = 4;
#		else
		const Int32 MAX_CACHE_COUNT = 1024;
#		endif

        IMessageStorage m_IMessageStorage = null;

		public void Initialize(HttpContext context)
		{
			lock (m_Lock)
			{
                string path = HttpContext.Current == null || HttpContext.Current.Request.ApplicationPath == "/" ? "/IM" : HttpContext.Current.Request.ApplicationPath + "/IM";
                Configuration config = WebConfigurationManager.OpenWebConfiguration(path);
				string accStorage = config.AppSettings.Settings["MessageStorageImpl"].Value;
				String[] accStorageInfo = accStorage.Split(new char[] { ' ' });
				Type type = Assembly.Load(accStorageInfo[0]).GetType(accStorageInfo[1]);
				ConstructorInfo ctor = type.GetConstructor(new Type[] { });
				m_IMessageStorage = ctor.Invoke(new object[] { }) as IMessageStorage;

				m_MaxKey = m_IMessageStorage.GetMaxKey();
				m_MaxCreatedTime = m_IMessageStorage.GetCreatedTime();
			}
		}

		public void Dispose()
		{
		}

		public List<Message> FindInDatabase(String receiver, String sender, Nullable<DateTime> from)
		{
			lock (m_Lock)
			{
				return m_IMessageStorage.Find(
					receiver == "*" ? 0 : AccountImpl.Instance.GetUserInfo(receiver).ID,
					sender == "*" ? 0 : AccountImpl.Instance.GetUserInfo(sender).ID,
					from
				);
			}
		}

		/// <summary>
		/// 插入新的消息，插入消息后将查询m_Listeners中是否有符合条件的监听器，如存在，同时将消息发送出去
		/// </summary>
		public Message NewMessage(String receiver, String sender, String content, Hashtable data)
		{
			lock (m_Lock)
			{
				Int64 key = ++m_MaxKey;
				content = HtmlUtil.ReplaceHtml(content);
				MsgAccessoryEval eval = new MsgAccessoryEval(key, receiver, sender, data);
				Regex reg = new Regex("{Accessory [^\f\n\r\t\v<>]+}");
				content = reg.Replace(content, eval.Replace);


				Message message = new Message(
					AccountImpl.Instance.GetUserInfo(sender),
					AccountImpl.Instance.GetUserInfo(receiver),
					content, new DateTime((DateTime.Now.Ticks / 10000) * 10000), key
				);

				List<Message> messages = new List<Message>();
				messages.Add(message);

				if (AccountImpl.Instance.GetUserInfo(receiver).Type == 0)
				{
					if (SessionManagement.Instance.IsOnline(receiver))
					{
						try
						{
							AccountState state = SessionManagement.Instance.GetAccountState(receiver);
							Hashtable config = state.GetConfig("message.conf");

							lock (config)
							{
								DateTime lrt = (DateTime)config["LastReceivedTime"];
								if (lrt < message.CreatedTime) config["LastReceivedTime"] = message.CreatedTime;
							}

							string cmdData = Utility.RenderHashJson(
								"Peer", sender,
								"Message", message
							);

							SessionManagement.Instance.Send(receiver, "GLOBAL:IM_MESSAGE_NOTIFY", cmdData);
						}
						catch
						{
						}
					}
				}
				else
				{
					AccountInfo groupInfo = AccountImpl.Instance.GetUserInfo(receiver);
					AccountInfo senderInfo = AccountImpl.Instance.GetUserInfo(sender);
					foreach (string member in groupInfo.Friends)
					{
						try
						{
							AccountInfo memberInfo = AccountImpl.Instance.GetUserInfo(member);
							if (senderInfo.Name.ToLower() != "administrator" && memberInfo.ID == senderInfo.ID) continue;
							if (SessionManagement.Instance.IsOnline(member))
							{
								AccountState state = SessionManagement.Instance.GetAccountState(memberInfo.Name);
								Hashtable config = state.GetConfig("message.conf");

								lock (config)
								{
									DateTime lrt = (DateTime)config["LastReceivedTime"];
									if (lrt < message.CreatedTime) config["LastReceivedTime"] = message.CreatedTime;
								}

								string cmdData = Utility.RenderHashJson(
									"Peer", groupInfo.Name,
									"Message", message
								);

								SessionManagement.Instance.Send(member, "GLOBAL:IM_MESSAGE_NOTIFY", cmdData);
							}
						}
						catch
						{
						}
					}
				}

				MessageCacheManagement.Instance.Insert(receiver, message);

				if (MessageCacheManagement.Instance.Count >= MAX_CACHE_COUNT)
				{
                    WriteCache();
				}

				return message;
			}
		}

        public void WriteCache()
        {
            if (MessageCacheManagement.Instance.Count > 0)
            {
                List<Message> cacheMsgs = MessageCacheManagement.Instance.GetAll();
				m_IMessageStorage.Write(cacheMsgs);
                MessageCacheManagement.Instance.Clear();
            }
        }

		public List<Message> FindHistory(String receiver, String sender, DateTime from, DateTime to)
		{
			return m_IMessageStorage.FindHistory(
				AccountImpl.Instance.GetUserInfo(receiver).ID,
				AccountImpl.Instance.GetUserInfo(sender).ID,
				from, to
			);
		}

		/// <summary>
		/// 添加消息监听器，如果查找到符合监听器条件的消息，返回false，此时不会添加监听器
		/// 如果没有查找到符合监听器条件的消息，返回true，此时监听器将被添加到m_Listeners中
		/// </summary>
		public List<Message> Find(String receiver, String sender, Nullable<DateTime> from)
		{
			lock (m_Lock)
			{
				//获取用户receiver缓存的消息的最小发送时间
				Nullable<DateTime> min = MessageCacheManagement.Instance.GetMinCreatedTime(receiver);

				List<Message> messages = new List<Message>();

				//当from >= 缓存在内存中的消息的最小时间时，不必查询数据库
				if (min == null || from == null || from.Value < min.Value)
				{
					//查询数据库
					messages.AddRange(FindInDatabase(receiver, sender, from));
					if (AccountImpl.Instance.GetUserInfo(receiver).Type == 0)
					{
						messages.AddRange(FindInDatabase(sender, receiver, from));
					}
				}

				//在缓存中查询
				messages.AddRange(MessageCacheManagement.Instance.Find(receiver, sender, from.Value));
				if (AccountImpl.Instance.GetUserInfo(receiver).Type == 0)
				{
					messages.AddRange(MessageCacheManagement.Instance.Find(sender, receiver, from.Value));
				}

				return messages;
			}
		}
	}

	public class MessageCacheManagement
	{
		static MessageCacheManagement m_Instance = new MessageCacheManagement();

		static public MessageCacheManagement Instance
		{
			get { return m_Instance; }
		}

		private MessageCacheManagement()
		{
		}

		Int32 m_Count = 0;
		Hashtable m_Cache = new Hashtable();

		List<Message> GetUserMessageCache(String user)
		{
			if (!m_Cache.ContainsKey(user))
			{
				m_Cache.Add(user, new List<Message>());
			}

			return m_Cache[user] as List<Message>;
		}

		/// <summary>
		/// 清除缓存
		/// </summary>
		public void Clear()
		{
			lock (m_Cache)
			{
				List<Message> msgs = new List<Message>();
				foreach (DictionaryEntry ent in m_Cache)
				{
					(ent.Value as List<Message>).Clear();
				}
				m_Count = 0;
			}
		}

		/// <summary>
		/// 获取所有缓存的消息
		/// </summary>
		/// <returns></returns>
		public List<Message> GetAll()
		{
			lock (m_Cache)
			{
				List<Message> msgs = new List<Message>();
				foreach (DictionaryEntry ent in m_Cache)
				{
					foreach (Message msg in ent.Value as List<Message>)
					{
						msgs.Add(msg);
					}
				}
				return msgs;
			}
		}

		/// <summary>
		/// 获取某一用户缓存的消息的最小时间 
		/// </summary>
		public Nullable<DateTime> GetMinCreatedTime(string user)
		{
			lock (m_Cache)
			{
				List<Message> userMsgs = GetUserMessageCache(user);
				return userMsgs.Count == 0 ? null : new Nullable<DateTime>(userMsgs[0].CreatedTime);
			}
		}


		/// <summary>
		/// 在缓存中插入一条消息
		/// </summary>
		/// <param name="user"></param>
		/// <param name="msg"></param>
		public void Insert(String user, Message msg)
		{
			List<Message> userMsgs = null;

			lock (m_Cache)
			{
				userMsgs = GetUserMessageCache(user);
			}

			lock (userMsgs)
			{
				userMsgs.Add(msg);
				m_Count++;
			}
		}

		/// <summary>
		/// 查找缓存中接受者为user，发送时间大于from的消息
		/// </summary>
		public List<Message> Find(String user, String sender, DateTime from)
		{
			List<Message> msgs = new List<Message>();

			{
				List<Message> userMsgs = null;

				lock (m_Cache)
				{
					userMsgs = GetUserMessageCache(user);
				}

				lock (userMsgs)
				{

					int i = 0;
					while (i < userMsgs.Count && userMsgs[i].CreatedTime <= from) i++;

					while (i < userMsgs.Count)
					{
						if (sender == null || sender == "*" || sender == userMsgs[i].Sender.Name) msgs.Add(userMsgs[i]);
						i++;
					}
				}
			}

			if (sender == null || sender == "*")
			{
				//在缓冲中查找群消息
				AccountInfo userInfo = AccountImpl.Instance.GetUserInfo(user);
				foreach (string groupName in userInfo.Groups)
				{
					AccountInfo groupInfo = AccountImpl.Instance.GetUserInfo(groupName);

					List<Message> groupMsgs = null;
					lock (m_Cache)
					{
						groupMsgs = GetUserMessageCache(groupName);
					}

					lock (groupMsgs)
					{
						if (from < groupInfo.GetGroupMemberRenewTime(user))
						{
							from = groupInfo.GetGroupMemberRenewTime(user);
						}

						int i = 0;
						while (i < groupMsgs.Count && groupMsgs[i].CreatedTime <= from) i++;

						while (i < groupMsgs.Count)
						{
							msgs.Add(groupMsgs[i]);
							i++;
						}
					}
				}
			}
			return msgs;
		}

		/// <summary>
		/// 获取消息总量
		/// </summary>
		public Int32 Count
		{
			get { return m_Count; }
		}
	}

	internal class MsgAccessoryEval
	{
		string m_Receiver, m_Sender;
		string m_ReceiverMsgDir, m_SenderMsgDir, m_MsgDir;
		Hashtable m_Data;

		public MsgAccessoryEval(Int64 key, String receiver, String sender, Hashtable data)
		{
			m_Receiver = receiver;
			m_Sender = sender;

			m_Data = data;

			m_ReceiverMsgDir = string.Format("/{0}/Message/MSG{1:00000000}", receiver, key);
			m_SenderMsgDir = string.Format("/{0}/Message/MSG{1:00000000}", sender, key);
			m_MsgDir = AccountImpl.Instance.GetUserInfo(receiver).Type == 1 ? string.Format("/{1}/Message/MSG{0:00000000}", key, receiver) : string.Format("Message/MSG{0:00000000}", key);
		}

		public string Replace(Match match)
		{
			XmlDocument xml = new XmlDocument();
			string value = match.Value;
			xml.LoadXml(string.Format("<{0} />", value.Substring(1, value.Length - 2)));

			string src = GlobalObject.unescape(xml.DocumentElement.GetAttribute("src"));
			string type = xml.DocumentElement.GetAttribute("type").ToLower();
			string data = xml.DocumentElement.GetAttribute("data");

			bool isPublic = ServerImpl.Instance.IsPublic(src);

			if (isPublic)
			{
				//公共资源不拷贝
				return String.Format("{0}", Path.GetRelativePath(src));
			}
			else
			{
				if (!Directory.Exists(m_ReceiverMsgDir)) Directory.CreateDirectory(m_ReceiverMsgDir);
				if (AccountImpl.Instance.GetUserInfo(m_Receiver).Type == 0)
				{
					if (!Directory.Exists(m_SenderMsgDir)) Directory.CreateDirectory(m_SenderMsgDir);
				}

				string fileOwnerName = Path.GetUser(src);
				if (String.IsNullOrEmpty(fileOwnerName))
				{
					fileOwnerName = m_Sender;
					src = String.Format("/{0}/{1}", fileOwnerName, src);
				}

				bool allowRead=true;
				try
				{
					ServerImpl.Instance.CheckPermission(HttpContext.Current, src, IOPermission.Read);
				}
				catch
				{
					allowRead = false;
				}

				if (data != "" || allowRead)
				{
					Hashtable _files = new Hashtable();

					string fileName;
					if (!_files.ContainsKey(src))
					{
						fileName = Path.GetFileName(src);
						int i = 1;
						while (_files.ContainsValue(fileName))
						{
							fileName = string.Format("{0}({1}){2}", System.IO.Path.GetFileNameWithoutExtension(fileName), i.ToString(), System.IO.Path.GetExtension(fileName));
							i++;
						}

						_files.Add(src, fileName);

						try
						{
							if (AccountImpl.Instance.GetUserInfo(m_Receiver).Type == 0)
							{
								if (data == "")
								{
									File.Copy(src, m_SenderMsgDir + "/" + fileName);
								}
								else
								{
									String dataBase64 = m_Data[data] as String;
									Byte[] buffer = System.Convert.FromBase64String(dataBase64);

									using (System.IO.Stream stream = File.Open(m_SenderMsgDir + "/" + fileName, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None))
									{
										try
										{
											stream.Write(buffer, 0, buffer.Length);
										}
										finally
										{
											stream.Close();
										}

									}
								}
							}
							if (data == "")
							{
								File.Copy(src, m_ReceiverMsgDir + "/" + fileName);
							}
							else
							{
								String dataBase64 = m_Data[data] as String;
								Byte[] buffer = System.Convert.FromBase64String(dataBase64);

								using (System.IO.Stream stream = File.Open(m_ReceiverMsgDir + "/" + fileName, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None))
								{
									try
									{
										stream.Write(buffer, 0, buffer.Length);
									}
									finally
									{
										stream.Close();
									}

								}
							}
						}
						catch
						{
						}

					}
					else
						fileName = _files[src] as string;

					string newUrl = GlobalObject.escape(string.Format("{0}/{1}", m_MsgDir, fileName));

					return newUrl;
				}
				else
				{
					return src;
				}
			}
		}
	}
}
